#include "media_frame.h"

#include <map>
#include <string.h>
#include <ctime>

#include "utils_string_ex.h"
#include "utils_log.h"
#include "utils_mem.h"

namespace El {
namespace Media {

MediaFrame::~MediaFrame()
{
    // 释放内存池分配的内存
    if (poolData_ != nullptr) {
        Utils::Buddy::GetInstance().Free(poolData_);
        poolData_ = nullptr;
        poolDataSize_ = 0;
    }
}

bool MediaFrame::SetSize(int32_t size)
{
    // 释放内存池分配的内存
    if (poolData_ != nullptr) {
        Utils::Buddy::GetInstance().Free(poolData_);
        poolData_ = nullptr;
        poolDataSize_ = 0;
        usedSize_ = 0; // Reset used size
    }

    // 分配内存池内存
    poolData_ = Utils::Buddy::GetInstance().Malloc(size);
    if (poolData_ == nullptr) {
        EL_DEBUG("MediaFrame: Failed to allocate memory from pool, size: {}", size);
        return false;
    }
    poolDataSize_ = size;
    usedSize_ = 0; // Reset used size
    return true;
}

bool MediaFrame::PutBuffer(const uint8_t *data, int32_t length)
{
    if (data == nullptr || length <= 0) {
        EL_WARN("MediaFrame: Invalid input data or length.");
        return false;
    }

    if (poolData_ == nullptr) {
        EL_WARN("MediaFrame: Buffer not allocated. Call SetSize first.");
        return false;
    }

    if (usedSize_ + length > poolDataSize_) {
        EL_WARN("MediaFrame: Not enough space in buffer. Current size: {}, requested: {}, capacity: {}", usedSize_,
                 length, poolDataSize_);
        return false;
    }

    // Copy data to the buffer at the current offset
    memcpy(static_cast<uint8_t *>(poolData_) + usedSize_, data, length);
    usedSize_ += length; // Update the used size

    return true;
}

uint8_t *MediaFrame::GetBuffer() const
{
    return static_cast<uint8_t *>(poolData_);
}

int32_t MediaFrame::GetLength() const
{
    return usedSize_;
}

void MediaFrame::SetChannel(int32_t channel)
{
    ch_ = channel;
}

int32_t MediaFrame::GetChannel() const
{
    return ch_;
}

void MediaFrame::SetFrameType(uint32_t type)
{
    type_ = type;
}

uint32_t MediaFrame::GetFrameType() const
{
    return type_;
}

uint64_t MediaFrame::GetPts() const
{
    return pts_;
}

void MediaFrame::SetPts(uint64_t pts)
{
    pts_ = pts;
}

uint64_t MediaFrame::GetUtc() const
{
    return utc_;
}

void MediaFrame::SetUtc(uint64_t utc)
{
    utc_ = utc;
}

void MediaFrame::SetSeq(uint32_t seq)
{
    seq_ = seq;
}

uint32_t MediaFrame::GetSeq() const
{
    return seq_;
}

void MediaFrame::SetFrameRate(double frame_rate)
{
    frame_rate_ = frame_rate;
}

double MediaFrame::GetFrameRate() const
{
    return frame_rate_;
}

void MediaFrame::SetCodecType(uint32_t type)
{
    codec_type_ = type;
}

uint32_t MediaFrame::GetCodecType() const
{
    return codec_type_;
}

bool MediaFrame::IsVideoFrame() const
{
    return type_ == MEDIA_FRAME_I || type_ == MEDIA_FRAME_P || type_ == MEDIA_FRAME_B;
}

bool MediaFrame::IsAudioFrame() const
{
    return type_ == MEDIA_FRAME_PCM || type_ == MEDIA_FRAME_AAC || type_ == MEDIA_FRAME_G711A ||
           type_ == MEDIA_FRAME_G711U || type_ == MEDIA_FRAME_OPUS;
}

std::string MediaFrame::ToString() const
{
    const static std::map<int32_t, std::string> g_frameString = {
        {MEDIA_FRAME_UNKOWN, "UNKOWN"}, // 未知类型
        {MEDIA_FRAME_I, "I"},           // I帧
        {MEDIA_FRAME_P, "P"},           // P帧
        {MEDIA_FRAME_B, "B"},           // B帧
        {MEDIA_FRAME_MJPEG, "MJPEG"},   // MJPEG帧
        {MEDIA_FRAME_JPEG, "JPEG"},     // JPEG帧
        {MEDIA_FRAME_PCM, "PCM"},       // 原始音频帧
        {MEDIA_FRAME_AAC, "AAC"},       // AAC音频帧
        {MEDIA_FRAME_G711A, "G711A"},   // G711A音频帧
        {MEDIA_FRAME_G711U, "G711U"},   // G711U音频帧
        {MEDIA_FRAME_OPUS, "OPUS"},     // OPUS音频帧
    };

    return std::string("MediaFrame(")
        .append("ch:")
        .append(std::to_string(ch_))
        .append(" utc:")
        .append(Utils::utc64ToString(utc_))
        .append(" pts:")
        .append(std::to_string(pts_))
        .append(" seq:")
        .append(std::to_string(seq_))
        .append(" type:")
        .append(g_frameString.find(type_)->second)
        .append(" len:")
        .append(std::to_string(GetLength()))
        .append(")");
}

} // namespace Media
} // namespace El